﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_FIXED_TYPE_H
#define _UXS_FIXED_TYPE_H

#pragma once

#include "Types.h"
#include "AllFunc.h"
#include "../CompileTime/TemplateUtils.h"

namespace UX_STL
{
namespace UXTypes
{

#	define _FIXED_OPERATOR_E( _op, _ret1, _func1, _ret2, _func2, _ret3, _func3 ) \
									 _ret1 operator _op (const Self& right) const			_func1 \
		template <typename T>		 _ret2 operator _op (const T& right) const				_func2 \
		template <typename T> friend _ret3 operator _op (const T& left, const Self& right)	_func3 \

#	define _FIXED_OPERATOR( _op ) \
		_FIXED_OPERATOR_E( _op, \
				Self,	{ return Self(*this) _op##= right; }, \
				Self,	{ return Self(*this) _op##= right; }, \
				T,		{ return left _op (T)right; } ) \

#	define _FIXED_CMP_OPERATOR( _op ) \
				bool operator _op (const int& right) const					{ return GetInt() _op right; } \
				bool operator _op (const float_t& right) const				{ return GetFloat() _op right; } \
		friend	bool operator _op (const int& left, const Self& right)		{ return left _op right.GetInt(); } \
		friend	bool operator _op (const float_t& left, const Self& right)	{ return left _op right.GetFloat(); } \



	//
	// Fixed Point Value
	//

	template <typename I, typename U, typename IT, typename FT>
	struct TFixed
	{
	// types
	public:
		typedef TFixed<I,U,IT,FT>	Self;
		typedef I					half_int_t;
		typedef U					half_uint_t;
		typedef IT					int_t;
		typedef FT					float_t;


	// variables
	private:
		union {
			struct {
				half_int_t		_int;
				half_uint_t		_frac;
			};
			int_t				_value;
		};

		static const uint		_s_uFracRoundOffset;


	// methods
	public:

		// constructors
		TFixed () : _int(0), _frac(0)							{}
		TFixed (const Self &f) : _int(f._int), _frac(f._frac)	{}
		TFixed (const I& A, const U& B) : _int(A), _frac(B)		{}

		explicit TFixed (const int &val)						{ Set( val ); }
		explicit TFixed (const float &val)						{ Set( val ); }
		explicit TFixed (const double &val)						{ Set( val ); }
	

		// set/get
		int_t	GetU() const								{ return _value; }
		int_t &	GetU()										{ return _value; }
		int		GetInt() const								{ return _int + (_frac>>_s_uFracRoundOffset); }
		float_t	GetFloat() const;
		void	Set(const int &val);
		void	Set(const float &val);
		void	Set(const double &val);


		// type cast
		operator int () const								{ return GetInt(); }
		operator uint () const								{ return GetInt(); }
		operator float_t () const							{ return GetFloat(); }


		// unary operators
		Self &	operator ++ ()								{ ++_int;  return *this; }
		Self &	operator -- ()								{ --_int;  return *this; }
		Self	operator ++ (int) 							{ Self ret(*this);  ++_int;  return ret; }
		Self	operator -- (int)							{ Self ret(*this);  --_int;  return ret; }
		bool	operator !  () const						{ return not GetInt(); }
		Self	operator +  () const						{ return Self( +_int, _frac ); }
		Self	operator -  () const						{ return Self( -_int, _frac ); }
		Self	operator ~  () const						{ Self  ret;  ret._value = ~_value;  return ret; }
	

		// binary operators
		bool	operator == (const Self& right) const		{ return _value == right._value; }
		bool	operator != (const Self& right) const		{ return _value != right._value; }
		bool	operator >  (const Self& right) const		{ return (_int > right._int) or (_int == right._int and ((_int >= 0 and _frac > right._frac) or (_int < 0 and _frac < right._frac))) ; }
		bool	operator >= (const Self& right) const		{ return ( *this > right or *this == right ); }
		bool	operator <  (const Self& right) const		{ return not ( *this >= right ); }
		bool	operator <= (const Self& right) const		{ return not ( *this > right ); }

		Self &	operator =  (const int& right)				{ Set( right );				return *this; }
		Self &	operator += (const int& right)				{ Set( GetInt() + right );  return *this; }
		Self &	operator -= (const int& right)				{ Set( GetInt() - right );  return *this; }
		
		Self &	operator =  (const float_t& right)			{ Set( right );				  return *this; }
		Self &	operator += (const float_t& right)			{ Set( GetFloat() + right );  return *this; }
		Self &	operator -= (const float_t& right)			{ Set( GetFloat() - right );  return *this; }
		
		Self &	operator =  (const Self& right)				{ _value = right._value;	  return *this; }
		Self &	operator += (const Self& right)				{ Set( GetFloat() + right );  return *this; }
		Self &	operator -= (const Self& right)				{ Set( GetFloat() - right );  return *this; }

		template <typename T>	Self& operator *=  (const T& right)		{ Set( GetFloat() * float_t(right) );  return *this; }
		template <typename T>	Self& operator /=  (const T& right)		{ Set( GetFloat() / float_t(right) );  return *this; }
		template <typename T>	Self& operator %=  (const T& right)		{ Set( ::_hidden_::fmod( GetFloat(), float_t(right) ) );  return *this; }
		
		template <typename T>	Self&	operator |=  (const T& right)		{ _value |= int_t(right);  return *this; }
		template <typename T>	Self&	operator &=  (const T& right)		{ _value &= int_t(right);  return *this; }
		template <typename T>	Self&	operator ^=  (const T& right)		{ _value ^= int_t(right);  return *this; }
		/*template <>*/			Self&	operator |=  (const Self& right)	{ _value |= right._value;  return *this; }
		/*template <>*/			Self&	operator &=  (const Self& right)	{ _value &= right._value;  return *this; }
		/*template <>*/			Self&	operator ^=  (const Self& right)	{ _value ^= right._value;  return *this; }

		template <typename T>	TFixed&	operator >>= (const T& right)	{ _int >>= int(right);  return *this; }
		template <typename T>	TFixed&	operator <<= (const T& right)	{ _int <<= int(right);  return *this; }


		_FIXED_OPERATOR( + );
		_FIXED_OPERATOR( - );
		_FIXED_OPERATOR( * );
		_FIXED_OPERATOR( / );
		_FIXED_OPERATOR_E( %,
				Self,	{ return ::_hidden_::fmod( GetFloat(), float_t(right) ); },
				Self,	{ return ::_hidden_::fmod( GetFloat(), right ); },
				T,		{ return ::_hidden_::fmod( left, T(right) ); }
		);
		_FIXED_OPERATOR( | );
		_FIXED_OPERATOR( & );
		_FIXED_OPERATOR_E( ^,
				Self,	{ return Self(*this) ^= right; },
				Self,	{ return Self(*this) ^= right; },
				T,		{ return left ^ T(right); }
		);
		_FIXED_OPERATOR_E( >>,
				Self,	{ return Self(*this) >>= right; },
				Self,	{ return Self(*this) >>= right; },
				T,		{ return left >> T(right); }
		);
		_FIXED_OPERATOR_E( <<,
				Self,	{ return Self(*this) <<= right; },
				Self,	{ return Self(*this) <<= right; },
				T,		{ return left << T(right); }
		);
		_FIXED_CMP_OPERATOR( == );
		_FIXED_CMP_OPERATOR( != );
		_FIXED_CMP_OPERATOR( >= );
		_FIXED_CMP_OPERATOR( >  );
		_FIXED_CMP_OPERATOR( <= );
		_FIXED_CMP_OPERATOR( <  );


		// checks //
		STATIC_ASSERT( sizeof(IT) == 2*sizeof(I) and sizeof(I) == sizeof(U), "incorrect types" );
	};
	


	template <typename I, typename U, typename IT, typename FT>
	const uint		TFixed<I,U,IT,FT>::_s_uFracRoundOffset	= CompileTime::SizeOf<U>::bits - 1;


	typedef TFixed< int32, uint32, int64, double >		fixed64_t;
	typedef TFixed< int16, uint16, int32, float >		fixed32_t;
	typedef TFixed< int8,  uint8,  int16, float >		fixed16_t;
	typedef	fixed32_t									fixed_t;


	
	template <typename I, typename U, typename IT, typename FT>
	inline void TFixed<I,U,IT,FT>::Set(const int &val)
	{
		_int  = half_int_t(val);
		_frac = 0;
	}

	
	template <typename I, typename U, typename IT, typename FT>
	inline void TFixed<I,U,IT,FT>::Set(const float &val)
	{
		float const	f = UXMath::Floor(val);
		float const d = UXMath::Abs(val) - UXMath::Abs(f);
	
		_int  = half_int_t ( f );
		_frac = half_uint_t( d * MaxValue<half_uint_t>() + FT(0.5) );
	}

	
	template <typename I, typename U, typename IT, typename FT>
	inline void TFixed<I,U,IT,FT>::Set(const double &val)
	{
		double const f = UXMath::Floor(val);
		double const d = UXMath::Abs(val) - UXMath::Abs(f);

		_int  = half_int_t( f );
		_frac = half_uint_t( d * MaxValue<half_uint_t>() + FT(0.5) );
	}
	
	
	template <typename I, typename U, typename IT, typename FT>
	inline FT TFixed<I,U,IT,FT>::GetFloat() const
	{
		return ( float_t(_int) + float_t(_frac) / float_t( MaxValue<half_uint_t>() ) );
	}
	
	
#	undef _FIXED_OPERATOR
#	undef _FIXED_OPERATOR_E
#	undef _FIXED_CMP_OPERATOR

//-------------------------------------------------------------------

}	// UXTypes
}	// UX_STL

#endif	// _UXS_FIXED_TYPE_H